'''
Created on 2011-07-04

@author: Andrew Roth
'''
class MultiCounter(object):
    def __init__(self, counters, min_samples_covered):
        self._counters = counters
        self._min_samples_covered = min_samples_covered
        
        all_refs = []
        for counter in counters:
            all_refs.append(set(counter.refs))
        
        self.refs = sorted(set.intersection(*all_refs))
        
    def iter_ref(self, ref):
        iters = []
        
        for counter in self._counters:
            iters.append(counter.iter_ref(ref))
        
        mulit_iter = MultiCounterIterator(iters, self._min_samples_covered)
        
        return mulit_iter 
    
class MultiCounterIterator(object):
    def __init__(self, iters, min_samples_covered):
        self._iters = iters
        
        self._min_samples_covered = min_samples_covered
        
        num_iters = len(iters)
        
        self._max_samples_uncovered = num_iters - min_samples_covered
        self._live_iters = range(num_iters)
        
        self._position = None
    
    def __iter__(self):
        return self
    
    def next(self):
        self._rows = []
        
        for iter in self._iters:
            self._rows.append(iter.next())
                
        self._position = min([row.position for row in self._rows ])
        
        while True:
            eq, gt = self._count_matches()
            
            # Enough samples covered so we return
            if eq >= self._min_samples_covered:
                return self._get_covered_rows()
            elif gt >= self._max_samples_uncovered:
                self._advance_to_next_position()
            else:
                self._advance_iters()

    def _count_matches(self):
        eq = 0
        gt = 0
        
        position = self._position
        
        for row in self._rows:
            if row.position == position:
                eq += 1
            elif row.position > position:
                gt += 1
        
        return eq, gt
    
    def _get_covered_rows(self):
        rows = []
        
        for row in self._rows:
            if row.position == self._position:
                rows.append(row)                            
            else:
                rows.append(None)
                
        multi_row = MultiCounterRow(self._position, rows)
        
        return multi_row
    
    def _advance_iters(self):
        '''
        Move every iterator which is behind the current position one step forward.
        '''
        for i, (row, iter) in enumerate(zip(self._rows, self._iters)):
            if i not in self._live_iters:
                continue
            
            if row.position < self._position:
                try:
                    self._rows[i] = iter.next()
                except StopIteration:
                    self._live_iters.remove(i)
    
    def _advance_to_next_position(self):
        '''
        Move current position forward by looking at all iters with positions greater and taking the min.
        '''
        candidate_positions = []
        
        for i, (row, iter) in enumerate(zip(self._rows, self._iters)):
            if row.position <= self._position:
                row = self._advance_iter_past_position(i)
            
            candidate_positions.append(row.position)
                
        self._position = min(candidate_positions)
        
    def _advance_iter_past_position(self, index):
        while True:
            row = self._iters[index].next()
            
            if row.position > self._position:
                self._rows[index] = row
                
                return row
            
class MultiCounterRow(object):
    def __init__(self, position, rows):
        self.position = position
        self.rows = rows
        
        self._init_depth()
        
    def _init_depth(self):
        depths = [row.depth for row in self.rows]
        
        self.depth = min(depths)
    
    def __str__(self):
        return "\t".join([str(x) for x in self.rows])
                
        
                 
